/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.solr.response.transform;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.JavaBinCodec;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.SolrCore;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.response.SolrQueryResponse;
import org.junit.BeforeClass;
import org.junit.Test;

public class TestSubQueryTransformer extends SolrTestCaseJ4 {
  private static int peopleMultiplier;
  private static int deptMultiplier;

  @BeforeClass
  public static void beforeTests() throws Exception {
    System.setProperty("solr.index.updatelog.enabled", "false");
    initCore("solrconfig-basic.xml", "schema-docValuesJoin.xml");
    peopleMultiplier = atLeast(1);
    deptMultiplier = atLeast(1);

    int id = 0;
    for (int p = 0; p < peopleMultiplier; p++) {
      assertU(
          add(
              doc(
                  "id",
                  "" + id++,
                  "name_s",
                  "john",
                  "title_s",
                  "Director",
                  "dept_ss_dv",
                  "Engineering",
                  "dept_i",
                  "0",
                  "dept_is",
                  "0")));
      assertU(
          add(
              doc(
                  "id",
                  "" + id++,
                  "name_s",
                  "mark",
                  "title_s",
                  "VP",
                  "dept_ss_dv",
                  "Marketing",
                  "dept_i",
                  "1",
                  "dept_is",
                  "1")));
      assertU(
          add(
              doc(
                  "id",
                  "" + id++,
                  "name_s",
                  "nancy",
                  "title_s",
                  "MTS",
                  "dept_ss_dv",
                  "Sales",
                  "dept_i",
                  "2",
                  "dept_is",
                  "2")));
      assertU(
          add(
              doc(
                  "id",
                  "" + id++,
                  "name_s",
                  "dave",
                  "title_s",
                  "MTS",
                  "dept_ss_dv",
                  "Support",
                  "dept_ss_dv",
                  "Engineering",
                  "dept_i",
                  "3",
                  "dept_is",
                  "3",
                  "dept_is",
                  "0")));
      assertU(
          add(
              doc(
                  "id",
                  "" + id++,
                  "name_s",
                  "tina",
                  "title_s",
                  "VP",
                  "dept_ss_dv",
                  "Engineering",
                  "dept_i",
                  "0",
                  "dept_is",
                  "0")));

      if (rarely()) {
        assertU(commit("softCommit", "true"));
      }
    }

    for (int d = 0; d < deptMultiplier; d++) {
      assertU(
          add(
              doc(
                  "id",
                  "" + id,
                  "id_i",
                  "" + id++,
                  "dept_id_s",
                  "Engineering",
                  "text_t",
                  "These guys develop stuff",
                  "salary_i_dv",
                  "1000",
                  "dept_id_i",
                  "0")));
      assertU(
          add(
              doc(
                  "id",
                  "" + id++,
                  "id_i",
                  "" + id++,
                  "dept_id_s",
                  "Marketing",
                  "text_t",
                  "These guys make you look good",
                  "salary_i_dv",
                  "1500",
                  "dept_id_i",
                  "1")));
      assertU(
          add(
              doc(
                  "id",
                  "" + id,
                  "id_i",
                  "" + id++,
                  "dept_id_s",
                  "Sales",
                  "text_t",
                  "These guys sell stuff",
                  "salary_i_dv",
                  "1600",
                  "dept_id_i",
                  "2")));
      assertU(
          add(
              doc(
                  "id",
                  "" + id,
                  "id_i",
                  "" + id++,
                  "dept_id_s",
                  "Support",
                  "text_t",
                  "These guys help customers",
                  "salary_i_dv",
                  "800",
                  "dept_id_i",
                  "3")));

      if (rarely()) {
        assertU(commit("softCommit", "true"));
      }
    }
    assertU(commit());
  }

  @Test
  public void testJohnOrNancySingleField() {
    // System.out.println("p "+peopleMultiplier+" d "+deptMultiplier);
    assertQ(
        "subq1.fl is limited to single field",
        req(
            "q",
            "name_s:(john nancy)",
            "indent",
            "true",
            "fl",
            "dept_ss_dv,name_s_dv,depts:[subquery]",
            "rows",
            "" + (2 * peopleMultiplier),
            "depts.q",
            "{!term f=dept_id_s v=$row.dept_ss_dv}",
            "depts.fl",
            "text_t",
            "depts.indent",
            "true",
            "depts.rows",
            "" + deptMultiplier),
        "count(//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts'][@numFound='"
            + deptMultiplier
            + "']/doc/str[@name='text_t'][.='These guys develop stuff'])="
            + (peopleMultiplier * deptMultiplier),
        "count(//result/doc/str[@name='name_s_dv'][.='nancy']/../result[@name='depts'][@numFound='"
            + deptMultiplier
            + "']/doc/str[@name='text_t'][.='These guys sell stuff'])="
            + (peopleMultiplier * deptMultiplier),
        "count((//result/doc/str[@name='name_s_dv'][.='john']/..)[1]/result[@name='depts']/doc[1]/*)=1",
        "count((//result/doc/str[@name='name_s_dv'][.='john']/..)[1]/result[@name='depts']/doc["
            + deptMultiplier
            + "]/*)=1",
        "count((//result/doc/str[@name='name_s_dv'][.='john']/..)["
            + peopleMultiplier
            + "]/result[@name='depts'][@numFound='"
            + deptMultiplier
            + "']/doc[1]/*)=1",
        "count((//result/doc/str[@name='name_s_dv'][.='john']/..)["
            + peopleMultiplier
            + "]/result[@name='depts'][@numFound='"
            + deptMultiplier
            + "']/doc["
            + deptMultiplier
            + "]/*)=1");
  }

  final String[] johnAndNancyParams =
      new String[] {
        "q",
        "name_s:(john nancy)",
        "indent",
        "true",
        "fl",
        "dept_ss_dv,name_s_dv,depts:[subquery]",
        "fl",
        "dept_i_dv,depts_i:[subquery]",
        "rows",
        "" + (2 * peopleMultiplier),
        "depts.q",
        "{!term f=dept_id_s v=$row.dept_ss_dv}",
        "depts.fl",
        "text_t",
        "depts.indent",
        "true",
        "depts.rows",
        "" + deptMultiplier,
        "depts_i.q",
        "{!term f=dept_id_i v=$row.dept_i_dv}",
        "depts_i.fl",
        "text_t", // multi val subquery param check
        "depts_i.fl",
        "dept_id_s_dv",
        "depts_i.indent",
        "true",
        "depts_i.rows",
        "" + deptMultiplier
      };

  @Test
  public void testTwoSubQueriesAndByNumberWithTwoFields() {
    final SolrQueryRequest johnOrNancyTwoFL = req(johnAndNancyParams);

    assertQ(
        "call subquery twice a row, once by number, with two fls via multival params",
        johnOrNancyTwoFL,
        "count(//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts']/doc/str[@name='text_t'][.='These guys develop stuff'])="
            + (peopleMultiplier * deptMultiplier),
        "count(//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts_i']/doc/str[@name='dept_id_s_dv'][.='Engineering'])="
            + (peopleMultiplier * deptMultiplier),
        "count(//result/doc/str[@name='name_s_dv'][.='nancy']/../result[@name='depts_i']/doc/str[@name='text_t'][.='These guys sell stuff'])="
            + (peopleMultiplier * deptMultiplier),
        "count(//result/doc/str[@name='name_s_dv'][.='nancy']/../result[@name='depts_i']/doc/str[@name='dept_id_s_dv'][.='Sales'])="
            + (peopleMultiplier * deptMultiplier),
        "count((//result/doc/str[@name='name_s_dv'][.='john']/..)["
            + peopleMultiplier
            + "]/result[@name='depts_i']/doc["
            + deptMultiplier
            + "]/str[@name='dept_id_s_dv'][.='Engineering'])=1",
        "count((//result/doc/str[@name='name_s_dv'][.='john']/..)["
            + peopleMultiplier
            + "]/result[@name='depts_i']/doc["
            + deptMultiplier
            + "]/str[@name='text_t'][.='These guys develop stuff'])=1");
  }

  @Test
  public void testRowsStartForSubqueryAndScores() throws Exception {

    String johnDeptsIds =
        h.query(
            req(
                new String[] {
                  "q",
                  "{!join from=dept_ss_dv to=dept_id_s}name_s:john",
                  "wt",
                  "csv",
                  "csv.header",
                  "false",
                  "fl",
                  "id",
                  "rows",
                  "" + deptMultiplier,
                  "sort",
                  "id_i desc"
                }));

    ArrayList<Object> deptIds = Collections.list(new StringTokenizer(johnDeptsIds));

    final int a = random().nextInt(deptMultiplier + 1);
    final int b = random().nextInt(deptMultiplier + 1);
    final int start = Math.min(a, b);
    final int toIndex = Math.max(a, b);
    List<Object> expectIds = deptIds.subList(start, toIndex);
    ArrayList<String> assertions = new ArrayList<>();
    // count((//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts'])[1]/doc/str[@name='id'])
    // random().nextInt(peopleMultiplier);
    assertions.add(
        "count((//result/doc/str[@name='name_s_dv'][.='john']/.."
            + "/result[@name='depts'][@numFound='"
            + deptMultiplier
            + "'][@start='"
            + start
            + "'])["
            + (random().nextInt(peopleMultiplier) + 1)
            + "]/doc/str[@name='id'])="
            + (toIndex - start));

    // System.out.println(expectIds);

    for (int i = 0; i < expectIds.size(); i++) {
      // (//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts'])[1]/doc[1]/str[@name='id']='15'
      String ithDoc =
          "(//result/doc/str[@name='name_s_dv'][.='john']/.."
              + "/result[@name='depts'][@numFound='"
              + deptMultiplier
              + "'][@start='"
              + start
              + "'])["
              + (random().nextInt(peopleMultiplier) + 1)
              + "]/doc["
              + (i + 1)
              + "]";
      assertions.add(ithDoc + "/str[@name='id'][.='" + expectIds.get(i) + "']");
      // let's test scores right there
      assertions.add(ithDoc + "/float[@name='score'][.='" + expectIds.get(i) + ".0']");
    }

    String[] john =
        new String[] {
          "q",
          "name_s:john",
          "indent",
          "true",
          "fl",
          "dept_ss_dv,name_s_dv,depts:[subquery]",
          "rows",
          "" + (2 * peopleMultiplier),
          "depts.q",
          "+{!term f=dept_id_s v=$row.dept_ss_dv}^=0 _val_:id_i",
          "depts.fl",
          "id",
          "depts.fl",
          "score",
          "depts.indent",
          "true",
          "depts.rows",
          "" + (toIndex - start),
          "depts.start",
          "" + start
        };

    assertQ(req(john), assertions.toArray(new String[] {}));
  }

  @Test
  public void testThreeLevel() throws Exception {
    List<String> asserts = new ArrayList<>();
    // dave works in both dept, get his coworkers from both
    for (String dept : new String[] {"Engineering", "Support"}) { // dept_id_s_dv">Engineering

      ArrayList<Object> deptWorkers =
          Collections.list(
              new StringTokenizer(
                  h.query(
                      req(
                          "q",
                          "dept_ss_dv:" + dept, // dept_id_i_dv
                          "wt",
                          "csv",
                          "csv.header",
                          "false",
                          "fl",
                          "name_s_dv",
                          "rows",
                          "" + peopleMultiplier * 3, // dave has three coworkers in two depts
                          "sort",
                          "id desc"))));
      // System.out.println(deptWorkers);

      // looping dave clones
      for (int p : new int[] {1, peopleMultiplier}) {
        // looping dept clones
        for (int d : new int[] {1, deptMultiplier}) {
          // looping coworkers
          int wPos = 1;
          for (Object mate : deptWorkers) {
            // (/response/result/doc/str[@name='name_s_dv'][.='dave']/..)[1]
            //  /result[@name='subq1']/doc/str[@name='dept_id_s_dv'][.='Engineering']/..
            //  /result[@name='neighbours']/doc/str[@name='name_s_dv'][.='tina']
            asserts.add(
                "((/response/result/doc/str[@name='name_s_dv'][.='dave']/..)["
                    + p
                    + "]"
                    + "/result[@name='subq1']/doc/str[@name='dept_id_s_dv'][.='"
                    + dept
                    + "']/..)["
                    + d
                    + "]"
                    + "/result[@name='neighbours']/doc["
                    + wPos
                    + "]/str[@name='name_s_dv'][.='"
                    + mate
                    + "']");
            wPos++;
          }
        }
      }
    }
    // System.out.println(asserts);
    assertQ(
        "dave works at both dept with other folks",
        //  System.out.println(h.query(
        req(
            new String[] {
              "q",
              "name_s:dave",
              "indent",
              "true",
              "fl",
              "dept_ss_dv,name_s_dv,subq1:[subquery]",
              "rows",
              "" + peopleMultiplier,
              "subq1.q",
              "{!terms f=dept_id_s v=$row.dept_ss_dv}",
              "subq1.fl",
              "dept_id_i_dv,text_t,dept_id_s_dv,neighbours:[subquery]",
              "subq1.indent",
              "true",
              "subq1.rows",
              "" + (deptMultiplier * 2),
              "subq1.neighbours.q", // flipping via numbers
              random().nextBoolean()
                  ? "{!terms f=dept_ss_dv v=$row.dept_id_s_dv}"
                  : "{!terms f=dept_is v=$row.dept_id_i_dv}",
              "subq1.neighbours.fl",
              "name_s_dv",
              "subq1.neighbours.rows",
              "" + peopleMultiplier * 3
            },
            "subq1.neighbours.sort",
            "id desc"),
        asserts.toArray(new String[] {}));
  }

  @Test
  public void testNoExplicitName() {
    String[] john =
        new String[] {
          "q",
          "name_s:john",
          "indent",
          "true",
          "fl",
          "name_s_dv," + "[subquery]",
          "rows",
          "" + (2 * peopleMultiplier),
          "depts.q",
          "+{!term f=dept_id_s v=$row.dept_ss_dv}^=0 _val_:id_i",
          "depts.fl",
          "id",
          "depts.fl",
          "score",
          "depts.indent",
          "true",
          "depts.rows",
          "" + deptMultiplier,
          "depts.start",
          "0"
        };

    assertQEx("no prefix, no subquery", req(john), ErrorCode.BAD_REQUEST);

    assertQEx(
        "no prefix, no subsubquery",
        req(
            "q",
            "name_s:john",
            "indent",
            "true",
            "fl",
            "name_s_dv," + "depts:[subquery]",
            "rows",
            "" + (2 * peopleMultiplier),
            "depts.q",
            "+{!term f=dept_id_s v=$row.dept_ss_dv}^=0 _val_:id_i",
            "depts.fl",
            "id",
            "depts.fl",
            "score",
            "depts.fl",
            "[subquery]", // <- here is a trouble
            "depts.indent",
            "true",
            "depts.rows",
            "" + deptMultiplier,
            "depts.start",
            "0"),
        ErrorCode.BAD_REQUEST);
  }

  @Test
  public void testDupePrefix() {
    assertQEx(
        "subquery name clash",
        req(
            new String[] {
              "q",
              "name_s:(john nancy)",
              "indent",
              "true",
              "fl",
              "name_s_dv,depts:[subquery]",
              "fl",
              "depts:[subquery]",
              "rows",
              "" + (2 * peopleMultiplier),
              "depts.q",
              "{!term f=dept_id_s v=$row.dept_ss_dv}",
              "depts.fl",
              "text_t",
              "depts.indent",
              "true",
              "depts.rows",
              "" + deptMultiplier,
              "depts_i.q",
              "{!term f=dept_id_i v=$depts_i.row.dept_i_dv}",
              "depts_i.fl",
              "text_t", // multi val subquery param check
              "depts_i.fl",
              "dept_id_s_dv",
              "depts_i.indent",
              "true",
              "depts_i.rows",
              "" + deptMultiplier
            }),
        ErrorCode.BAD_REQUEST);
  }

  @Test
  public void testJustJohnJson() throws Exception {

    final SolrQueryRequest johnTwoFL = req(johnAndNancyParams);
    ModifiableSolrParams params = new ModifiableSolrParams(johnTwoFL.getParams());
    params.set("q", "name_s:john");
    johnTwoFL.setParams(params);
    assertJQ(
        johnTwoFL,
        "/response/docs/[0]/depts/docs/[0]=={text_t:\"These guys develop stuff\"}",
        "/response/docs/["
            + (peopleMultiplier - 1)
            + "]/depts/docs/["
            + (deptMultiplier - 1)
            + "]=={text_t:\"These guys develop stuff\"}",
        "/response/docs/[0]/depts_i/docs/[0]=={dept_id_s_dv:\"Engineering\", text_t:\"These guys develop stuff\"}", // seem like key order doesn't matter , well
        "/response/docs/["
            + (peopleMultiplier - 1)
            + "]/depts_i/docs/["
            + (deptMultiplier - 1)
            + "]=="
            + "{text_t:\"These guys develop stuff\", dept_id_s_dv:\"Engineering\"}");
  }

  @SuppressWarnings("unchecked")
  @Test
  public void testJustJohnJavabin() throws Exception {
    final SolrQueryRequest johnTwoFL = req(johnAndNancyParams);
    ModifiableSolrParams params = new ModifiableSolrParams(johnTwoFL.getParams());
    params.set("q", "name_s:john");
    params.set("wt", "javabin");

    johnTwoFL.setParams(params);

    final NamedList<Object> unmarshalled;
    SolrCore core = johnTwoFL.getCore();
    SolrQueryResponse rsp = new SolrQueryResponse();
    SolrRequestInfo.setRequestInfo(new SolrRequestInfo(johnTwoFL, rsp));

    SolrQueryResponse response =
        h.queryAndResponse(johnTwoFL.getParams().get(CommonParams.QT), johnTwoFL);

    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    johnTwoFL.getResponseWriter().write(bytes, johnTwoFL, response);

    try (JavaBinCodec jbc = new JavaBinCodec()) {
      unmarshalled =
          (NamedList<Object>) jbc.unmarshal(new ByteArrayInputStream(bytes.toByteArray()));
    }

    johnTwoFL.close();
    SolrRequestInfo.clearRequestInfo();

    SolrDocumentList resultDocs = (SolrDocumentList) (unmarshalled.get("response"));

    Map<String, String> engText = new HashMap<>();
    engText.put("text_t", "These guys develop stuff");

    Map<String, String> engId = new HashMap<>();
    engId.put("text_t", "These guys develop stuff");
    engId.put("dept_id_s_dv", "Engineering");

    for (int docNum : new int[] {0, peopleMultiplier - 1}) {
      SolrDocument employeeDoc = resultDocs.get(docNum);
      assertEquals("john", employeeDoc.getFieldValue("name_s_dv"));
      for (String subResult : new String[] {"depts", "depts_i"}) {

        SolrDocumentList subDoc = (SolrDocumentList) employeeDoc.getFieldValue(subResult);
        for (int deptNum : new int[] {0, deptMultiplier - 1}) {
          SolrDocument deptDoc = subDoc.get(deptNum);
          Object expectedDept = (subResult.equals("depts") ? engText : engId);
          assertEquals("" + expectedDept + " equals to " + deptDoc, expectedDept, deptDoc);
        }
      }
    }
  }

  @Test
  public void testExceptionPropagation() {
    final SolrQueryRequest r =
        req(
            "q",
            "name_s:dave",
            "indent",
            "true",
            "fl",
            "depts:[subquery]",
            "rows",
            "" + (peopleMultiplier),
            "depts.q",
            "{!lucene}(",
            "depts.fl",
            "text_t",
            "depts.indent",
            "true",
            "depts.rows",
            "" + (deptMultiplier * 2),
            "depts.logParamsList",
            "q,fl,rows,subq1.row.dept_ss_dv");

    // System.out.println(h.query(r));

    assertQEx("wrong subquery", r, ErrorCode.BAD_REQUEST);

    assertQEx(
        "",
        req(
            "q",
            "name_s:dave",
            "indent",
            "true",
            "fl",
            "depts:[subquery]",
            "rows",
            "1",
            "depts.q",
            "{!lucene}",
            "depts.fl",
            "text_t",
            "depts.indent",
            "true",
            "depts.rows",
            "NAN",
            "depts.logParamsList",
            "q,fl,rows,subq1.row.dept_ss_dv"),
        ErrorCode.BAD_REQUEST);
  }

  @Test
  public void testMultiValue() {

    String[] happyPathAsserts =
        new String[] {
          "count(//result/doc/str[@name='name_s_dv'][.='dave']/../result[@name='subq1']/doc/str[@name='text_t'][.='These guys develop stuff'])="
              + (peopleMultiplier * deptMultiplier),
          "count(//result/doc/str[@name='name_s_dv'][.='dave']/../result[@name='subq1']/doc/str[@name='text_t'][.='These guys help customers'])="
              + (peopleMultiplier * deptMultiplier),
          "//result[@numFound=" + peopleMultiplier + "]"
        };
    Random random1 = random();

    assertQ(
        "dave works at both, whether we set a  default separator or both",
        req(
            new String[] {
              "q",
              "name_s:dave",
              "indent",
              "true",
              "fl",
              (random().nextBoolean() ? "name_s_dv,dept_ss_dv" : "*")
                  + ",subq1:[subquery "
                  + ((random1.nextBoolean() ? "" : "separator=,"))
                  + "]",
              "rows",
              "" + peopleMultiplier,
              "subq1.q",
              "{!terms f=dept_id_s v=$row.dept_ss_dv "
                  + ((random1.nextBoolean() ? "" : "separator=,"))
                  + "}",
              "subq1.fl",
              "text_t",
              "subq1.indent",
              "true",
              "subq1.rows",
              "" + (deptMultiplier * 2),
              "subq1.logParamsList",
              "q,fl,rows,row.dept_ss_dv"
            }),
        happyPathAsserts);

    assertQ(
        "even via numbers",
        req(
            "q",
            "name_s:dave",
            "indent",
            "true",
            "fl",
            "dept_is_dv,name_s_dv,subq1:[subquery]",
            "rows",
            "" + (peopleMultiplier),
            "subq1.q",
            "{!terms f=dept_id_i v=$row.dept_is_dv}",
            "subq1.fl",
            "text_t",
            "subq1.indent",
            "true",
            "subq1.rows",
            "" + (deptMultiplier * 2)),
        happyPathAsserts);

    assertQ(
        "even if we set a separator both",
        req(
            "q",
            "name_s:dave",
            "indent",
            "true",
            "fl",
            "dept_ss_dv,name_s_dv,name_s_dv,subq1:[subquery separator=\" \"]",
            "rows",
            "" + (peopleMultiplier),
            "subq1.q",
            "{!terms f=dept_id_s v=$row.dept_ss_dv separator=\" \"}",
            "subq1.fl",
            "text_t",
            "subq1.indent",
            "true",
            "subq1.rows",
            "" + (deptMultiplier * 2)),
        happyPathAsserts);

    String[] noMatchAtSubQ =
        new String[] {
          "count(//result/doc/str[@name='name_s_dv'][.='dave']/../result[@name='subq1'][@numFound=0])="
              + (peopleMultiplier),
          "//result[@numFound=" + peopleMultiplier + "]"
        };

    assertQ(
        "different separators, no match",
        req(
            "q",
            "name_s:dave",
            "indent",
            "true",
            "fl",
            "dept_ss_dv,name_s_dv,subq1:[subquery]",
            "rows",
            "" + (peopleMultiplier),
            "subq1.q",
            "{!terms f=dept_id_s v=$row.dept_ss_dv separator=\" \"}",
            "subq1.fl",
            "text_t",
            "subq1.indent",
            "true",
            "subq1.rows",
            "" + (deptMultiplier * 2)),
        noMatchAtSubQ);

    assertQ(
        "and no matter where",
        req(
            "q",
            "name_s:dave",
            "indent",
            "true",
            "fl",
            "dept_ss_dv,name_s_dv,subq1:[subquery separator=\" \"]",
            "rows",
            "" + (peopleMultiplier),
            "subq1.q",
            "{!terms f=dept_id_s v=$row.dept_ss_dv}",
            "subq1.fl",
            "text_t",
            "subq1.indent",
            "true",
            "subq1.rows",
            "" + (deptMultiplier * 2)),
        noMatchAtSubQ);

    assertQ(
        "setting a wrong parser gets you nowhere",
        req(
            "q",
            "name_s:dave",
            "indent",
            "true",
            "fl",
            "dept_ss_dv,name_s_dv,subq1:[subquery]",
            "rows",
            "" + (peopleMultiplier),
            "subq1.q",
            "{!term f=dept_id_s v=$row.dept_ss_dv}",
            "subq1.fl",
            "text_t",
            "subq1.indent",
            "true",
            "subq1.rows",
            "" + (deptMultiplier * 2)),
        noMatchAtSubQ);

    assertQ(
        "but it luckily works with default query parser, but it's not really reliable",
        req(
            "q",
            "name_s:dave",
            "indent",
            "true",
            "fl",
            "dept_ss_dv,name_s_dv,subq1:[subquery separator=\" \"]",
            "rows",
            "" + (peopleMultiplier),
            "subq1.q",
            "{!lucene df=dept_id_s v=$row.dept_ss_dv}",
            "subq1.fl",
            "text_t",
            "subq1.indent",
            "true",
            "subq1.rows",
            "" + (deptMultiplier * 2)),
        happyPathAsserts);

    assertQ(
        "even lucene qp can't help at any separator but space",
        req(
            "q",
            "name_s:dave",
            "indent",
            "true",
            "fl",
            "dept_ss_dv,name_s_dv,"
                + "subq1:[subquery "
                + (random().nextBoolean()
                    ? ""
                    : "separator=" + ((random().nextBoolean() ? "" : ",")))
                + "]",
            "rows",
            "" + (peopleMultiplier),
            "subq1.q",
            "{!lucene df=dept_id_s v=$row.dept_ss_dv}",
            "subq1.fl",
            "text_t",
            "subq1.indent",
            "true",
            "subq1.rows",
            "" + (deptMultiplier * 2)),
        noMatchAtSubQ);
  }
}
